package cn.gjculture.shopping.api.service.impl;

import cn.gjculture.shopping.api.dao.*;
import cn.gjculture.shopping.api.entity.*;
import cn.gjculture.shopping.api.export.ExportExcel;
import cn.gjculture.shopping.api.export.OrderExportExcel;
import cn.gjculture.shopping.api.service.*;
import cn.gjculture.shopping.api.util.DateUtil;
import cn.gjculture.shopping.api.util.SpringContent;
import cn.gjculture.shopping.api.util.UUIDUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
public class OrderServiceImpl implements OrderService {


    @Autowired
    OrderDao orderDao;

    @Autowired
    CustomerDao customerDao;

    @Autowired
    ScoreRecordDao scoreRecordDao;

    @Autowired
    CommonUpdateMapper commonUpdateMapper;

    @Autowired
    CashCouponDao cashCouponDao;

    @Autowired
    GoodsMapper goodsMapper;

    @Autowired
    PayService payService;

    @Autowired
    InterfaceService interfaceService;

    @Autowired
    OilCardService oilCardService;

    @Autowired
    RoutingRuleService routingRuleService;

    @Autowired
    MerchantService merchantService;

    @Autowired
    LogisticsDao logisticsDao;

    @Autowired
    LogisticsService logisticsService;

    @Autowired
    OrderMapper orderMapper;

    @Autowired
    AddressService addressService;

    @Transactional
    @Override
    public int save(Order order, Return<InterfaceParam> ret) {
        order.setOrderId(UUIDUtil.getUUID());
        long updateTime = DateUtil.getTimestamp();
        order.setInsertTime(updateTime);
        order.setUpdateTime(updateTime);
        order.setStatus(1);
        order.setOrderType(Order.OrderType.GOODS.getValue());
        //保存订单
        orderDao.insert(order);
        //保存售出商品
        List<GoodsRecord> goodsRecords = order.getGoodsRecords();
        Map<String, RoutingRule.Routing> routingMap = new HashMap<>();
        if (goodsRecords != null && goodsRecords.size() > 0) {
            for (GoodsRecord gr : goodsRecords) {
                gr.setGoodsRecordId(UUIDUtil.getUUID());
                gr.setInsertTime(updateTime);
                gr.setUpdateTime(updateTime);
            }
            goodsMapper.insertRecord(goodsRecords);
        }
        //积分使用保存
        this.saveUseScore(order, ret);
        //优惠券使用保存
        this.saveUseCashCoupon(order, ret);
        //支付
        List<RoutingRule.Routing> routings = new ArrayList<>();
        for (Map.Entry<String, RoutingRule.Routing> route : routingMap.entrySet()) {
            routings.add(route.getValue());
        }
        this.pay(order, routings, ret);
        return 1;
    }

    private List<RoutingRule.Routing> routing(Map<String, RoutingRule.Routing> routingMap, GoodsRecord gr, String orderSum, String orderTotalSum) {
        List<RoutingRule> routingRules = routingRuleService.queryByGoodsId(gr.getGoodsNo());
        List<RoutingRule.Routing> routings = new ArrayList<>();
        for (RoutingRule route : routingRules) {
            RoutingRule.Routing routing = new RoutingRule.Routing();
            //TODO 可能出现空指针
            routing.setMerNo(route.getMerchant().getSonMerNo());
            //按固定值分账
            if (route.getRouteType() == RoutingRule.RoutType.FIXED.getValue()) {
                //固定值*个数
                routing.setValue(new BigDecimal(route.getValue()).multiply(new BigDecimal(gr.getCount())));
                //按百分比分账
            } else if (route.getRouteType() == RoutingRule.RoutType.PERCENT.getValue()) {
                //按实际出售价分账
                if (route.getRootIn() == RoutingRule.RootIn.ACTUAL_PRICE.getValue()) {
                    //总优惠率
                    BigDecimal totalDiscount = new BigDecimal(orderTotalSum).divide(new BigDecimal(orderSum));
                    //实际卖出的价格=原价* 总折扣率
                    BigDecimal actualPrice = new BigDecimal(gr.getDiscountPrice()).multiply(totalDiscount);
                    //实际卖出的价格*个数*分账百分比
                    routing.setValue(actualPrice.multiply(new BigDecimal(gr.getCount()))//实际卖出的价格*个数
                            .multiply(new BigDecimal(Integer.valueOf(route.getValue()) / 100)));//分账百分比
                    //按原价分账
                } else if (route.getRootIn() == RoutingRule.RootIn.SELLING_PRICE.getValue()) {
                    //原价*分账百分比*个数
                    routing.setValue(new BigDecimal(gr.getDiscountPrice())//原价
                            .multiply(new BigDecimal(Integer.valueOf(route.getValue()) / 100))//分账百分比
                            .multiply(new BigDecimal(gr.getCount())));//个数
                }
            }
            routings.add(routing);
        }
        for (RoutingRule.Routing route : routings) {
            if (routingMap.containsKey(route.getMerNo())) {
                routingMap.get(route.getMerNo()).setValue(routingMap.get(route.getMerNo()).getValue().multiply(route.getValue()));
            } else {
                routingMap.put(route.getMerNo(), route);
            }
        }
        return routings;
    }

    /**
     * 支付
     *
     * @param order
     * @param routings
     * @param ret
     */
    private void pay(Order order, List<RoutingRule.Routing> routings, Return<InterfaceParam> ret) {
        Pay pay = new Pay();
        pay.setCustomerId(order.getCustomerId());
        pay.setPayId(UUIDUtil.getUUID());
        pay.setInsertTime(DateUtil.getTimestamp());
        pay.setAmount(order.getOrderTotalSum());
        pay.setOrderId(order.getOrderId());
        pay.setCustomerId(order.getCustomerId());
        pay.setPayType(Pay.ThirdType.QYF.getValue());
        payService.insert(pay);
        InterfaceParam payParam = interfaceService.getPayParam(order, routings);
        ret.setData(payParam);
        ret.getCommon().setRet(true);
    }

    /**
     * 保存使用的优惠券
     *
     * @param order
     * @param ret
     */
    private void saveUseCashCoupon(Order order, Return<InterfaceParam> ret) {
        List<CashCoupon> cashCoupons = order.getCashCoupons();
        if (cashCoupons != null && cashCoupons.size() > 0) {
            for (CashCoupon cc : cashCoupons) {
                cc.setStatus(3);
                cc.setUpdateTime(order.getUpdateTime());
                commonUpdateMapper.updateCashCoupon(cc);
                CashCouponRecord ccRecored = new CashCouponRecord();
                ccRecored.setCashCouponRecordId(UUIDUtil.getUUID());
                ccRecored.setCashCouponId(cc.getCashCouponId());
                ccRecored.setOrderId(order.getOrderId());
                ccRecored.setUseTime(order.getUpdateTime());
                cashCouponDao.insertRecord(ccRecored);
            }
        }
    }

    /**
     * 保存使用的积分
     *
     * @param order
     * @param ret
     */
    private void saveUseScore(Order order, Return ret) {

    }

    @Override
    public int saveHotel(Order order, Return<InterfaceParam> ret) {
        order.setOrderId(UUIDUtil.getUUID());
        long updateTime = DateUtil.getTimestamp();
        order.setInsertTime(updateTime);
        order.setUpdateTime(updateTime);
        order.setStatus(1);
        order.setOrderType(Order.OrderType.HOTEL.getValue());
        //保存订单
        orderDao.insert(order);
        //保存售出商品
        List<GoodsRecord> goodsRecords = order.getGoodsRecords();
        Map<String, RoutingRule.Routing> routingMap = new HashMap<>();
        if (goodsRecords != null && goodsRecords.size() > 0) {
            for (GoodsRecord gr : goodsRecords) {
                gr.setGoodsRecordId(UUIDUtil.getUUID());
                gr.setUpdateTime(updateTime);
                gr.setInsertTime(updateTime);
                gr.setOrderId(order.getOrderId());
                List<RoutingRule.Routing> routings = this.routing(routingMap, gr, order.getOrderSum(), order.getOrderTotalSum());
                for (RoutingRule.Routing route : routings) {
                    if (routingMap.containsKey(route.getMerNo())) {
                        routingMap.get(route.getMerNo()).setValue(routingMap.get(route.getMerNo()).getValue().multiply(route.getValue()));
                    } else {
                        routingMap.put(route.getMerNo(), route);
                    }
                }
            }
            goodsMapper.insertRecord(goodsRecords);
            //TODO 下库存
        }
        //积分使用保存
        this.saveUseScore(order, ret);
        //优惠券使用保存
        this.saveUseCashCoupon(order, ret);
        //支付
        List<RoutingRule.Routing> routings = new ArrayList<>();
        for (Map.Entry<String, RoutingRule.Routing> route : routingMap.entrySet()) {
            routings.add(route.getValue());
        }
        this.pay(order, routings, ret);
        return 1;
    }

    @Override
    public List<Order> queryByCustomerId(String customerId) {
        List<Order> orders = orderDao.queryByCustomerId(customerId);
        for (Order o : orders) {
            collect(o);
        }
        return orders;
    }

    @Override
    public String unifyOrder(UnifyParam unifyParam) {
        Order order = new Order(true);
        order.setOrderType(unifyParam.getOrderType());
        order.setOrderTotalSum(unifyParam.getPayAmount());
        order.setOrderSubject(unifyParam.getOrderSubject());
        Customer customer = customerDao.queryByOpenid(unifyParam.getOpenid());
        if (customer != null) {
            order.setCustomerId(customer.getId());
        }
        GoodsRecord record = new GoodsRecord(true);
        record.setOrderId(order.getOrderId());
        record.setCount(1);
        record.setCommodityType(getOrderTypeToCommodityTy(order.getOrderType()));
        record.setDiscountPrice(order.getOrderTotalSum());
        List<GoodsRecord> records = new ArrayList<>(1);
        records.add(record);
        goodsMapper.insertRecord(records);
        Merchant merchant = merchantService.queryByBelongId(unifyParam.getHotelId());
        unifyParam.setMerchantNo(merchant.getSonMerNo());
        orderDao.insert(order);
        unifyParam.setOrderId(order.getOrderId());
        payService.unifyPay(unifyParam);
        return null;
    }

    @Override
    public List<Order> query(OrderParams params) {
        params.setOrderType(Order.OrderType.GOODS.getValue());
        List<Order> orders = orderMapper.query(params);
        for (Order order : orders) {
            collect(order);
        }
        return orders;
    }

    @Override
    public int updateStatus(Order order) {
        order.setUpdateTime(DateUtil.getTimestamp());
        return orderDao.updateStatus(order);
    }

    @Override
    public int update(Order order) {
        order.setUpdateTime(DateUtil.getTimestamp());
        return orderMapper.update(order);
    }

    @Override
    public RefundFlow queryRefundFlow(String orderId) {
        return orderDao.queryRefundFlow(orderId);
    }

    @Override
    public int handleRefund(RefundFlow refundFlow, Return ret) {
        if (refundFlow.getStatus() == 3 || refundFlow.getStatus() == 4) {
            refundFlow.setAgreeTime(DateUtil.getTimestamp());
        } else if (refundFlow.getStatus() == 5) {
            refundFlow.setLogisticsTime(DateUtil.getTimestamp());
        } else if (refundFlow.getStatus() == 6) {
            refundFlow.setRefundTime(DateUtil.getTimestamp());
        }
        boolean isRefund = false;
        if ((refundFlow.getRefundType() == RefundFlow.RefundType.ONLY_REFUND.getValue() && refundFlow.getStatus() == 4) ||
                (refundFlow.getRefundType() == RefundFlow.RefundType.REFUND_AND_GOODS.getValue() && refundFlow.getStatus() == 6)) {
            isRefund = true;
            refundFlow.setRefundTime(DateUtil.getTimestamp());
        }
        orderMapper.updateRefundFlow(refundFlow);
        Order order=new Order();
        order.setOrderId(refundFlow.getOrderId());
        order.setRefundStatus(refundFlow.getStatus());
        order.setStatus(null);
        order.setUpdateTime(DateUtil.getTimestamp());
        orderMapper.update(order);

        if (isRefund) {
            SubAccount sub=new SubAccount();
            sub.setOrderId(order.getOrderId());
            sub.setStatus(3);
            payService.updateSubAccountByOrderId(sub);
            SupplierSubAccount supplierSubAccount=new SupplierSubAccount();
            supplierSubAccount.setOrderId(order.getOrderId());
            supplierSubAccount.setStatus(3);
            payService.updateSupplierSubAccount(supplierSubAccount);
            this.refund(order, ret);
        }
        return 1;
    }

    @Override
    public List<Order> queryOilBuy() {
        List<Order> orders=orderDao.queryByOrderType(Order.OrderType.OIL_CARD_BUY.getValue());
        for (Order order : orders) {
            collect(order);
        }
        return orders;
    }

    @Override
    public List<Order> queryOil() {
        List<Order> orders=orderDao.queryByOrderType(Order.OrderType.OIL_CARD_CHARGE.getValue());
        for (Order order : orders) {
            collect(order);
        }
        return orders;
    }

    @Override
    public void export(OrderParams orderParams, HttpServletResponse response) throws IOException {
        List<Order> orders=orderMapper.query(orderParams);
        if(!CollectionUtils.isEmpty(orders)){
            for(Order o:orders){
                collect(o);
                o.setAddress(addressService.queryById(o.getAddressId()));
            }
        }
        exportOrder(orders,response);
    }

    private void exportOrder(List<Order> orders, HttpServletResponse response) throws IOException {
        ExportExcel exportExcel= SpringContent.getBean(OrderExportExcel.class);
        exportExcel.setDatas(orders);
        exportExcel.setSheetName("订单列表");
        exportExcel.export(response.getOutputStream());
    }

    private Integer getOrderTypeToCommodityTy(Integer orderType) {
        if (orderType == Order.OrderType.GOODS.getValue()) {
            return Commodity.CommodityType.GOODS.getValue();
        } else if (orderType == Order.OrderType.HOTEL.getValue()) {
            return Commodity.CommodityType.HOTEL.getValue();
        } else if (orderType == Order.OrderType.OIL_CARD_CHARGE.getValue() || orderType == Order.OrderType.OIL_CARD_BUY.getValue()) {
            return Commodity.CommodityType.OIL_CARD.getValue();
        }
        return -1;
    }

    private void collect(Order o) {
        List<GoodsRecord> goodsRecords = orderDao.queryGoodsRecordByOrderId(o.getOrderId());
        if (goodsRecords != null && goodsRecords.size() > 0) {
            for (GoodsRecord goodsRecord : goodsRecords) {
                goodsRecord.setLogistics(logisticsService.queryByGoodsRecordId(goodsRecord.getGoodsRecordId()));
            }
            o.setGoodsRecords(goodsRecords);
        }
        o.setInsertTimeStr(DateUtil.getDate(o.getInsertTime()));
        o.setUpdateTimeStr(DateUtil.getDate(o.getUpdateTime()));
        o.setCustomer(customerDao.queryById(o.getCustomerId()));
        addONToOrderNo(o);
    }

    @Override
    public int insertOilBuy(Order order, Return<InterfaceParam> ret) {
        //插入订单数据
        order.setOrderId(UUIDUtil.getUUID());
        order.setInsertTime(DateUtil.getTimestamp());
        order.setUpdateTime(DateUtil.getTimestamp());
        order.setOrderType(Order.OrderType.OIL_CARD_BUY.getValue());
        order.setStatus(1);
        orderDao.insert(order);
        //插入支付信息
        this.pay(order, null, ret);
        return 0;
    }

    @Override
    public int insertOilCash(Order order, String customerId, String customerName, String phone, String cardNo, Return<InterfaceParam> ret) {
        order.setOrderId(UUIDUtil.getUUID());
        long updateTime = DateUtil.getTimestamp();
        order.setInsertTime(updateTime);
        order.setUpdateTime(updateTime);
        order.setOrderType(Order.OrderType.OIL_CARD_CHARGE.getValue());
        order.setStatus(1);
        //保存订单
        orderDao.insert(order);
        if (oilCardService.queryByPhone(phone) != null) {
            this.pay(order, null, ret);
        } else {
            OilCard oilCard = new OilCard();
            oilCard.setCardNo(cardNo);
            oilCard.setCustomerId(customerId);
            oilCard.setPhone(phone);
            oilCard.setCustomerName(customerName);
            oilCardService.insert(oilCard);

            Pay pay = new Pay();
            pay.setCustomerId(customerId);
            pay.setAmount(order.getOrderTotalSum());
            pay.setOrderId(order.getOrderId());
            payService.insert(pay);
            InterfaceParam payParam = interfaceService.getPayParam(order, null);
            ret.setData(payParam);
            ret.getCommon().setRet(true);
        }
        return 1;
    }

    @Override
    public int refund(Order order, Return ret) {
        Order o = this.queryById(order.getOrderId());
        if (o != null) {
            boolean isRefund = payService.refund(o);
            handlerSubAccount(order.getGoodsRecords());
        } else {
            ret.getCommon().setError("不能办理退款");
        }
        return 0;
    }

    private void handlerSubAccount(List<GoodsRecord> goodsRecords) {
        if(!CollectionUtils.isEmpty(goodsRecords)){
            List<String> ids=new ArrayList<>();
            for(GoodsRecord goodsRecord:goodsRecords){
                ids.add(goodsRecord.getGoodsRecordId());
            }
            orderMapper.updateSubAccountByGoodsRecord(ids);
            orderMapper.updateSupplierSubAccountByGoodsRecord(ids);
        }
    }


    public Order queryById(String orderId) {
        Order order=orderDao.queryById(orderId);
        collect(order);
        return order;
    }


    private void addONToOrderNo(Order o){
        o.setOrderNoStr("ON"+o.getOrderNo());
    }


}
